import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;

class Arena {
	public ArrayList<Player> players;
	byte[] sendData;
	byte[] receiveData;
	DatagramSocket receptionSocket, broadcastSocket;

	public Arena(int port) throws SocketException {
		this.players = new ArrayList<Player>();
		this.receiveData = new byte[1024];
		this.sendData = new byte[1024];
		this.receptionSocket = new DatagramSocket(port);
		this.broadcastSocket = new DatagramSocket(port-1);
	}

	public void start() throws IOException, InterruptedException {
		System.out.println("Server started on " + receptionSocket.getLocalAddress().getHostName() + ":" + Integer.toString(receptionSocket.getLocalPort()));
		System.out.println("type help for commands");
		Thread receptionThread = new Thread() {
		    public void run() {
		        try {
		    		while (true) {
		    			DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
						receptionSocket.receive(receivePacket);
						registerPacket(receivePacket);
						Thread.sleep(70);
		    		}
		        } catch (SocketException e) {
					e.printStackTrace();
					receptionSocket.close();
				} catch (IOException e) {
					e.printStackTrace();
				} catch (InterruptedException e) {
				}
		    }
		};
		receptionThread.start();
		
		Thread arenaThread = new Thread() {
		    public void run() {
		    	boolean done = false;	
				long delta = 0;	
				long start = System.currentTimeMillis();
				while (!done) {
					if (delta >= 14) {
						start = System.currentTimeMillis();
						prepareSnapshot();
						try {
							broadcastSnapshot();
						} catch (IOException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
						//Thread.sleep(5);
					}
					long end = System.currentTimeMillis();
					delta = end - start;
				}
		    }
		};
		arenaThread.start();

		
		//receptionSocket.close();
		//broadcastSocket.close();

	}

	public void handleCommands(String cmd) throws IOException {

		if (cmd.toLowerCase().equals("restart")){
			this.restart();
		}
		else if (cmd.toLowerCase().equals("help")) {
			String helpMessage = 
					"help    \t shows available commands for server" +
					"\n" +
					"restart \t restarts the arena";
			System.out.println(helpMessage);
		}
	}

	private void restart() {
		this.players.clear();
		System.out.println("arena is cleared");
	}

	private void registerPacket(DatagramPacket packet) {
		Player player = null;
		//Retrieve playerId
		int playerId = ByteBuffer.wrap(receiveData, 0, 4).order(ByteOrder.BIG_ENDIAN).getInt();
		
		for (Player p : this.players) {
			if (p.id == playerId)
			{
				player = p;
				break;
			}
		}

		//Register if new player
		if (player == null && players.size() <= 5) {
			player = new Player(packet.getAddress(), playerId);
			this.players.add(player);
			System.out.println("Subscribed player ID: " + playerId);
		}
	}

	public void broadcastSnapshot() throws IOException {

		for (Player p : this.players) {

			DatagramPacket sendPacket = new DatagramPacket(this.sendData,
					this.sendData.length, p.ipAddress, p.client_port);
			this.broadcastSocket.send(sendPacket);
			//System.out.println("Sent packet to: " + p.ipAddress.getHostAddress() + ":" + p.client_port);
		}
	}

	public void prepareSnapshot() {
		ByteBuffer target = ByteBuffer.wrap(this.sendData);
		for (int i = 0; i< this.players.size(); i++) {
			Player p = this.players.get(i);
			
			int ID = p.id;
			float X =  p.x;
			float Y =  p.y;;
			float rotation =  p.rotation;
			
			byte[] id = ByteBuffer.allocate(4).putInt(ID).array();
			target.put(id);
			byte[] x = ByteBuffer.allocate(4).putFloat(X).array();
			target.put(x); 
			byte[] y = ByteBuffer.allocate(4).putFloat(Y).array();
			target.put(y);	 
			byte[] rot = ByteBuffer.allocate(4).putFloat(rotation).array();
			target.put(rot); 	
			byte[] bullets = p.getBullets();
			target.put(bullets);
		}
		this.sendData = target.array();
	}
	
}